package com.zyj.adapter;

import com.zyj.annotation.ResponseBody;
import com.zyj.conversion.IntegerTypeHandler;
import com.zyj.conversion.StringTypeHandler;
import com.zyj.conversion.TypeHandler;
import com.zyj.handler.HandlerMethod;
import com.zyj.model.ModelAndView;
import com.zyj.utils.JsonUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 专门来处理注解开发的HandlerMethod处理器类型
 */
public class RequestMappingHandlerAdapter implements HandlerAdapter {
    private Map<Class, TypeHandler> typeHandlers = new HashMap<>();

    /**
     * 在适配器的构造方法中就初始化所有的参数类型处理器
     */
    public RequestMappingHandlerAdapter() {
        // 如果方法的当前参数类型是String类型，则使用StringTypeHandler String类型的参数处理器来处理参数
        this.typeHandlers.put(String.class, new StringTypeHandler());

        // 如果方法的当前参数类型是Integer类型，则使用IntegerTypeHandler Integer类型的参数处理器来处理参数
        this.typeHandlers.put(Integer.class, new IntegerTypeHandler());

        // 一次性建立所有类型的处理器
    }

    @Override
    public boolean supports(Object handler) {
        return (handler instanceof HandlerMethod);
    }

    @Override
    public ModelAndView handleRequest(Object handler, HttpServletRequest req, HttpServletResponse resp) throws Exception {
        // 注解开发方式下HandlerMethod类才是真正的处理器类型，才是真正处理请求的处理器
        HandlerMethod hm = (HandlerMethod) handler;
        // 从HandlerMethod取出封装好的controller和method
        Object controller = hm.getController();
        Method method = hm.getMethod();

        // 获取参数(参数解析)
        Object[] args = handleParameters(method, req);
        // 通过反射调用处理器方法处理请求
        // controller是bean对象，args是方法的参数值数组
        Object returnValue = method.invoke(controller, args);

        // 处理返回值
        ModelAndView modelAndView = handleReturnValue(returnValue, method, resp);
        return modelAndView;
    }

    /**
     * 处理返回值（ResponseBody）
     *
     * @param returnValue
     * @param method
     * @param resp
     * @return
     */
    private ModelAndView handleReturnValue(Object returnValue, Method method, HttpServletResponse resp) throws Exception {
        //需要根据方法对象判断当前方法是否被ResponseBody对象所修饰了，如果是的话，需要将对象类型的返回值转换为json格式进行返回
        if (method.isAnnotationPresent(ResponseBody.class)) {
            if (returnValue instanceof String) {
                resp.setContentType("text/plain;charset=utf8");
                resp.getWriter().write(returnValue.toString());
            } else if (returnValue instanceof Map) {
                resp.setContentType("application/json;charset=utf8");
                resp.getWriter().write(JsonUtils.object2Json(returnValue));
            }//......
        } else {
            // 如果当前方法没有被ResponseBody注解修饰

            // 视图处理
        }

        return new ModelAndView();
    }

    /**
     * 将Request请求中的参数，绑定到处理器方法的参数中
     * 将请求中的String[]类型的参数转为方法中所需要的参数类型
     * 其实本质上该方法就是解析请求中的在字符串数组类型的参数值
     *
     * @param method
     * @param req
     * @return
     * @throws Exception
     */
    private Object[] handleParameters(Method method, HttpServletRequest req) throws Exception {
        List results = new ArrayList();

        // 获取Request请求参数（Key其实对应的是Method方法参数的名称）
        Map<String, String[]> parameterMap = req.getParameterMap();

        // 获取到方法中的所有参数
        Parameter[] parameters = method.getParameters();

        // 遍历方法的所有参数，将请求中的该参数的参数值进行类型处理，转为方法所需要的类型
        for (Parameter parameter : parameters) {
            // 获取参数名称（先埋个雷）
            // 通过反编译之后，获取到的参数名称，不做特殊处理的话，name值是arg0，arg1这样的值
            String name = parameter.getName();

            // 根据参数名称获取到请求当中当前参数的参数值的数组
            String[] valueArray = parameterMap.get(name);
            if (valueArray == null || valueArray.length == 0) {
                results.add(null);
                continue;
            }

            // 处理参数的类型转换
            // 根据参数类型获取参数类型处理器
            TypeHandler typeHandler = typeHandlers.get(parameter.getType());

            // 调用指定的参数类型处理器来处理参数
            // valueArray 参数值的数组
            // value 转换完成的参数值
            Object value = typeHandler.handleValue(valueArray);

            results.add(value);
        }

        // 集合转数组
        return results.toArray();
    }

}
